#include "uart.h"


#define USART1_TX_PIN (GPIO_NUM_17)
#define USART1_RX_PIN (GPIO_NUM_18)
#define USART1_NUM (UART_NUM_1)
#define RX_BUF_SIZE   2500
#define Pack_Base     2048

uint8_t Usart1_RecvBuff[RX_BUF_SIZE] = {0};
uint8_t USB_Control = 0;//是否开启USB传图
uint16_t USB_DelayTime = 0;//每一列的打印间隔
uint8_t USB_Print_Stata = 0;//是否进入打印，防止还没传完图就进入打印
uart_event_t usart_ev;//创建一个串口事件
static QueueHandle_t usart_queue;//创建一个消息队列
//整体流程是  1.上位机告诉下位机开始 2.下位机向上位机请求数据包 3.上位机下发数据包，下位机接收  
//4.下位机请求的包数大于上位机总包数，上位机下发数据传输完成。5.下位机接收到数据传输完成，回复上位机数据传输完成。6.上位机接收到下位机数据接收完成，下发开始打印指令，指令数据为每一列的数据打印间隔
void USBControl_Pic(void){
	uint8_t RecFlag = 0;
	uint16_t Recv_CurrentStep = 0;//记录是第几个数据
	static uint16_t Recvdata_length = 0;//接收一共有多少个数据
	static uint8_t Recvdata_state = 0;//0,验证状态   1，数据接收状态
	static uint32_t data_move=7;//记录数据位置
	static uint16_t pack_index=0;//记录上位机发送的是第几包数据
	static uint32_t crc = 0;//计算校验
	static uint16_t CurrentLoadPage = 1;//计算第几个包数
	static uint16_t count = 0;//记录数据有多少个，用于调试使用
	if(pdTRUE == xQueueReceive(usart_queue,&usart_ev,0)){//如果从数据队列里面接收到消息
			switch(usart_ev.type){//判断串口事件类型
				case UART_DATA://是数据
					memset(Usart1_RecvBuff,0,usart_ev.size);//先清空数组
					uart_read_bytes(USART1_NUM,Usart1_RecvBuff,usart_ev.size,0);//读出来串口数据，usart_ev.size是接收到的数据长度
					//if(Debug_set)ESP_LOGE("USB", "Count :%d  move :%d  Size  :%d\n",count,data_move,usart_ev.size);
					if(!USB_Control){//如果没开启串口传图打印功能
						for(uint8_t i=0;i<usart_ev.size;i++){//循环检测数据
							if(Usart1_RecvBuff[i] == 0x5A)RecFlag = 0x80;
							else if((Usart1_RecvBuff[i] == 0xA5) && (RecFlag == 0x80))RecFlag = 0x40;
							else if((Usart1_RecvBuff[i] == 0x5A) && (RecFlag == 0x80))RecFlag = 0x80;
							else if((Usart1_RecvBuff[i] == 0x51) && (RecFlag == 0x40))RecFlag = 0xC0;
							else if(RecFlag == 0xC0){
								switch(Usart1_RecvBuff[i]){//经过数据校验
									case 0xA1://将测试图数据传进打印数组
										if((ResetESP32count < 5)&&(!Test_Printdata)){//没有复位ESP32并且没有执行该函数才进入
											Test_Printdata = 1;//标志位置一，在main中拷贝数据
										}
										if(Debug_set)ESP_LOGE("Test_PrintData", "Test_Printdata Open\n");
										break;
									case 0xA2:
										USB_Control = 1;ReadyPrintFlag=0;Beep_set(200);//打开USB功能，清除有图标志，不让打印
										if(Debug_set)ESP_LOGE("USB", "USB Open!!\n");
										break;
									default:break;
								}
								RecFlag = 0;
							}
							else RecFlag = 0;
						}
					}else{//开启串口传图打印功能
						if(usart_ev.size >= 120){//如果是整包120个数据，测试过，每次都是最大120个数据，底层定义的改不了，不这样写会有数据丢失
							if(Recvdata_state == 0){//如果是验证状态
								uint16_t Command = 0;//命令变量
								for(uint8_t i=0;i<7;i++){//6A A6 08（包头） 00 00(命令) 00 00（特定命令下的数据）
									if((Recv_CurrentStep==0) && (Usart1_RecvBuff[i] == 0x6A))Recv_CurrentStep++;
									else if((Recv_CurrentStep==1) && (Usart1_RecvBuff[i] == 0xA6))Recv_CurrentStep++;
									else if((Recv_CurrentStep==1) && (Usart1_RecvBuff[i] == 0x6A))Recv_CurrentStep=1;
									else if((Recv_CurrentStep==1) && (Usart1_RecvBuff[i] != 0x6A))Recv_CurrentStep=0;
									else if((Recv_CurrentStep==2) && (Usart1_RecvBuff[i] == 0x08))Recv_CurrentStep++;
									else if(Recv_CurrentStep==3){
										Command |=(uint16_t)Usart1_RecvBuff[i++];Command|=(uint16_t)Usart1_RecvBuff[i++]<<8;//取出命令，命令是01b1 发送端是b101大小端数据
										if(Command == CMD_LoadPage){//在命令里面。只有下载命令才能达到120
											if(Debug_set)ESP_LOGE("USB", "Command LoadPage\n");
											pack_index |= (uint16_t)Usart1_RecvBuff[i++];pack_index|=(uint16_t)Usart1_RecvBuff[i]<<8;//下载命令的特定数据，这次传输的是第几个包
											for(uint8_t j=0;j<usart_ev.size-7;j++){//前面七个数据是包头命令什么的，不是图片数据
												RxBuf[data_move+j] = Usart1_RecvBuff[j+7];
												crc += Usart1_RecvBuff[j+7];//切记校验不能把前面的包头带进去
											}	
											data_move += (usart_ev.size-7);//计算图片数据长度
											count++;//我自己计数用的
											Recvdata_state = 1;//置一代表数据接收状态，不进入函数解析包头
										}
										Recv_CurrentStep = 0;
									}
									else //验证未通过
										Recv_CurrentStep=0;
								}
							}
							else{//数据接收状态，直接将120个数据复制进打印数组
								for(uint8_t i=0;i<usart_ev.size;i++){
									RxBuf[data_move+i] = Usart1_RecvBuff[i];	
									crc += Usart1_RecvBuff[i];
								}
								data_move += usart_ev.size;
								count++;
							}
						}
						else{//如果不是整包数据的话，那就是带有crc校验的那一批或者纯指令。有可能会出现带CRC校验的刚好是120，这是漏洞，没想到怎么区分，遇到了再说
							if(Recvdata_state==0){
								uint16_t Command = 0;
								for(uint8_t i=0;i<7;i++){
									if((Recv_CurrentStep==0) && (Usart1_RecvBuff[i] == 0x6A))Recv_CurrentStep++;
									else if((Recv_CurrentStep==1) && (Usart1_RecvBuff[i] == 0xA6))Recv_CurrentStep++;
									else if((Recv_CurrentStep==1) && (Usart1_RecvBuff[i] == 0x6A))Recv_CurrentStep=1;
									else if((Recv_CurrentStep==1) && (Usart1_RecvBuff[i] != 0x6A))Recv_CurrentStep=0;
									else if((Recv_CurrentStep==2) && (Usart1_RecvBuff[i] == 0x08))Recv_CurrentStep++;
									else if(Recv_CurrentStep==3){
										Command |=(uint16_t)Usart1_RecvBuff[i++];Command|=(uint16_t)Usart1_RecvBuff[i]<<8;
										switch(Command){
											case CMD_Start://上位机先告诉我开始了
												USB_Print_Stata = 0;//传图过程中置零不让进入定时器的打印函数
												if(Debug_set)ESP_LOGE("USB", "Command 	Start\n");
												memset(RxBuf,0,data_length);//清空上一次数据长度的数据
												data_length = 0;//归零
												i++;//手动加一
												Recvdata_length |= (uint16_t)Usart1_RecvBuff[i++]<<8;Recvdata_length|=(uint16_t)Usart1_RecvBuff[i];//特定指令数据，接下来要发送的数据一共有多长
												if(Debug_set)ESP_LOGE("USB", "DataLength:%d\n",Recvdata_length);
												usart1_sendcommand(CMD_RquestLoad,CurrentLoadPage);//向上位机请求第一包数据
												break;
											case CMD_Finish://上位机告诉我数据传输完啦
												i=i+2;//循环固定写死七次，因为上面的start读取命令后有参数，读取的时候i加一，结束指令没有参数，需要自增2防止再次进入循环
												Recvdata_length = 0;
												data_length = data_move;//打印长度等于接收数据长度
												//if(Debug_set)ESP_LOGE("USB", "FINISH DataLength:%d count %d\n",data_length,INPulse_count);
												data_move = 0;
												pack_index = 0;
												CurrentLoadPage = 1;
												count = 0;
												usart1_sendcommand(CMD_Finish,0);//回复上位机数据传输完成
												Beep_set(200);
												ReadyPrintFlag = 1;//数据传输完成，有图了，标志位置一
												if(Debug_set)ESP_LOGE("USB", "USB finish!!\n");
												break;
											case CMD_Print://上位机告诉下位机开始打印
												if(Debug_set)ESP_LOGE("USB", "Command 	Print\n");
												i++;
												USB_DelayTime |= (uint16_t)Usart1_RecvBuff[i++];USB_DelayTime|=(uint16_t)Usart1_RecvBuff[i]<<8;
												USB_Print_Stata = 1;//数据传完了，可以进入定时器打印了
												gpio_intr_disable(Pluse_IO);//需要关闭外部中断，让滚轮打印失效，现在只能按照给的时间间隔打印
												StartPrintFlag = 1;					   	//开启打印，这个变量是按键控制的，直接开启打印
												LED5_ON;								   	//打印灯亮
												Beep_set(200);
												break;
											case CMD_Stop://上位机告诉我停止USB控制，回到手机传图和滚轮打印。以防万一，没具体确认哪些变量必须归零，哪些不需要，直接所有用到的变量全部归零。
												Recvdata_length = 0;//接收数据长度
												data_length = 0;//因为不确定是在传完图发的停止还是发图中间发的，打印长度也归零，不让打印
												data_move = 0;//移动距离归零
												pack_index = 0;//接收的包数归零
												CurrentLoadPage = 1;//请求包数置一
												count = 0;//计数归零
												ReadyPrintFlag = 0;//没有图片
												USB_Print_Stata = 0;//打印状态改为不打印
												USB_Control = 0;//USB传图功能修改为不开启
												INPulse_count = 0;//不确定脉冲数有没有影响，直接清零
												gpio_intr_enable(Pluse_IO);//打开脉冲中断，滚轮控制使能
												StartPrintFlag = 0;					   	//关闭打印
												LED5_OFF;//关闭打印指示灯
												Beep_set(200);
											break;
										}
										Recvdata_state = 0;//接收数据状态改为验证状态
										Recv_CurrentStep = 0;
									}
									else //验证未通过
										Recv_CurrentStep=0;
								}
							}else{//不是整包数据并且还是传图状态，那就是带校验位的
								for(uint8_t i=0;i<usart_ev.size-4;i++){//减四是因为校验位不算数据
									RxBuf[data_move+i] = Usart1_RecvBuff[i];
									crc += Usart1_RecvBuff[i];
								}
								data_move += (usart_ev.size-4);
								count++;
								//验证校验位
								uint32_t crc_read = 0;
								crc_read |= Usart1_RecvBuff[usart_ev.size-4]<<24;crc_read|=Usart1_RecvBuff[usart_ev.size-3]<<16;
								crc_read |= Usart1_RecvBuff[usart_ev.size-2]<<8;crc_read|=Usart1_RecvBuff[usart_ev.size-1];
								if(Debug_set)ESP_LOGE("USB", "CRC:%X   READ:%X\n",crc,crc_read);
								if(Debug_set)ESP_LOGE("USB", "%X %X %X %X\n",Usart1_RecvBuff[usart_ev.size-4],Usart1_RecvBuff[usart_ev.size-3],Usart1_RecvBuff[usart_ev.size-2],Usart1_RecvBuff[usart_ev.size-1]);
								if(crc_read == crc){//校验通过，页数加一清空状态和校验和
									CurrentLoadPage++;
									Recvdata_state = 0;//切换回校验状态
									crc = 0;
									if(Debug_set)ESP_LOGE("USB", "LoadPage:%d\n",CurrentLoadPage);
								}
								else{//验证没通过
									uint8_t Last_pack = 0;//判断最后一个包是哪一个包
									uint8_t Pack_eve_odd = 0;//判断最后一个包是不是整包
									if(Recvdata_length%Pack_Base){//最后一个包不是整包
										Last_pack = ((Recvdata_length / Pack_Base) + 1);
										Pack_eve_odd = 1;
									}else{
										Last_pack = Recvdata_length / Pack_Base;
										Pack_eve_odd = 0;
									}
									if(pack_index == Last_pack){//是最后一个包
										if(Pack_eve_odd)//最后一个包不是整包
											data_move -= (Recvdata_length%Pack_Base);//数据移动位置减去不是整包的长度
										else
											data_move -= Pack_Base;//否则减去整包
									}else{//不是最后一个包，校验失败也要减去整包数
										data_move -= Pack_Base;
									}
									Recvdata_state = 0;//切换回验证状态
									crc = 0;//校验归零
									if(Debug_set)ESP_LOGE("USB", "CRC ERROR %d\n",data_move);
								}
								usart1_sendcommand(CMD_RquestLoad,CurrentLoadPage);//通过校验请求下一包，没通过重新请求没通过的包数据							
							}	
						}
					}
					break;	
				default:break;
			}
	}
}

void usart1_init(void)
{
    //配置 UART 参数，包括波特率、数据位、校验位、停止位、流控和时钟源。
    const uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_APB
    };

    //应用 UART 参数配置。
    uart_param_config(USART1_NUM, &uart_config);
    //设置 UART 引脚，指定 TX 和 RX 引脚，其他引脚保持不变。
    uart_set_pin(USART1_NUM, USART1_TX_PIN, USART1_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
    //安装 UART 驱动程序，配置接收缓冲区大小为 RX_BUF_SIZE，不使用发送缓冲区。
    uart_driver_install(USART1_NUM, RX_BUF_SIZE*2, 0, 20, &usart_queue, 0);	
}

void usart1_sendcommand(uint16_t cmd,uint16_t index)
{    //串口发送指令  包头  指令  指令数据
	uint8_t add = 3;
	uint8_t Pack_hand[10]={0x6A,0xA6,0x05,0,0,0,0,0,0,0};
	Pack_hand[add++] = cmd & 0x0ff;
	Pack_hand[add++] = cmd >> 8;
	if((cmd == CMD_RquestLoad) && index)
	{
		Pack_hand[add++] = index & 0x0ff;
		Pack_hand[add++] = index >> 8;
	}
	uart_write_bytes(USART1_NUM,Pack_hand,add);
}
